/*    This file is part of libepistle.
 *
 *    libepistle is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    libepistle is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with libepistle.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <arpa/inet.h>
#include <string.h>

#include "err.h"
#include "transfer.h"
#include "message.h"

int
msg_copy(Msg *des, const Msg *src, Err *err)
{
	*des = *src;

	if (!src->buf_freeable)
		return 0;

	if (!(des->buf = malloc(src->len))) {
		err_std(err);
		return -1;
	}

	memcpy(des->buf, src->buf, src->len);
	return 0;
}

int
msg_extend(Msg *msg, uint32_t amt, Err *err)
{
	void *tmp;

	if (msg->buf_freeable) {
		if (!(tmp = realloc(msg->buf, msg->len + amt)))
			goto error;
	} else {
		if (!(tmp = malloc(msg->len + amt)))
			goto error;

		memcpy(tmp, msg->buf, msg->len);
	}

	msg->buf = tmp;
	msg->buf_freeable = 1;
	return 0;
error:
	err_std(err);
	return -1;
}

void
msg_send_init(Msg *msg, uint32_t len, void *buf, int buf_freeable)
{
	msg->len_tnsfd = msg->buf_tnsfd = 0;
	msg->len = len;
	msg->buf = buf;
	msg->buf_freeable = buf_freeable;
}

int
msg_send(int fd, Msg *msg, int wait, Err *err)
{
	if (msg->len_tnsfd < sizeof(msg->len)) {
		uint32_t net_len = htonl(msg->len);

		if (send_buf(&net_len, fd, sizeof(net_len),
			     &msg->len_tnsfd, wait, err) < 0)
		        return -1;

		if (msg->len_tnsfd < sizeof(msg->len))
			return 0;
	}

	if (msg->buf_tnsfd >= msg->len)
		return 0;

	return send_buf(msg->buf, fd, msg->len, &msg->buf_tnsfd, wait, err);
}

void
msg_recv_init(Msg *msg)
{
	msg->len_tnsfd = msg->buf_tnsfd = 0;
	msg->buf = NULL;
	msg->buf_freeable = 1;
}

int
msg_recv(int fd, Msg *msg, uint32_t max_len, int wait, Err *err)
{
	if (msg->len_tnsfd < sizeof(msg->len)) {
		if (recv_buf(&msg->len, fd, sizeof(msg->len),
			     &msg->len_tnsfd, wait, err) < 0)
			return -1;

		if (msg->len_tnsfd < sizeof(msg->len))
			return 0;

		msg->len = ntohl(msg->len);
	}

	if (msg->len > max_len) {
		err_epistle(err, EPISTLE_ERR_LEN);
		return -1;
	}

	if (msg->buf_tnsfd >= msg->len)
		return 0;

	if (!msg->buf && !(msg->buf = malloc(msg->len))) {
		err_std(err);
		return -1;
	}

	return recv_buf(msg->buf, fd, msg->len, &msg->buf_tnsfd, wait, err);
}

void
msg_dispose(Msg *msg)
{
	if (msg->buf_freeable)
		free(msg->buf);
}

int
msg_tnsfd(Msg *msg)
{
	return msg->len_tnsfd == sizeof(msg->len) &&
		msg->buf_tnsfd == msg->len;
}

void
msg_reuse(Msg *msg)
{
	msg->len_tnsfd = msg->buf_tnsfd = 0;
}
